home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Container Common / download.cpp < prev    next >
Text File  |  1997-01-03  |  27KB  |  1,145 lines

  1. //
  2. //  DOWNLOAD.CPP
  3. //
  4. //  Copyright (C) Microsoft Corporation, 1996
  5. //
  6.  
  7. #include "headers.h"
  8. #include "binhex.h"
  9. #include "urlapi.h"
  10. #include <Files.h>
  11. #include <Folders.h>
  12. #include <Resources.h>
  13. #include <CodeFragments.h>
  14. #include "urlmonsupport.h"
  15.  
  16. FORMATETC g_FileFormatEtc = {
  17.     CF_NULL,
  18.     NULL,
  19.     DVASPECT_CONTENT,
  20.     -1,
  21.     TYMED_FILE
  22. };
  23.  
  24. char g_szExplorerFolderName[] = "\pExplorer";
  25. char g_szCacheFolderName[] = "\pActiveX Cache";
  26. OLECHAR g_szFileVersion[] = "FileVersion";
  27. OLECHAR g_szDestDir[] = "destdir";
  28. OLECHAR g_szVersion[] = "version";
  29.  
  30. BOOL FindPrivateFolder(short *foundvRefNum, long *foundDirID, long ParentDirID, StringPtr FolderName);
  31. BOOL FindExplorerFolder(short *foundvRefNum, long *foundDirID);
  32. BOOL GetVersionFromString(LPOLESTR pszString, LPDWORD pdwVersion);
  33. BOOL CheckFileVersion(LPOLESTR pszFile, DWORD dwMinimumVersion, BOOL fUseExtensionsFolder);
  34. HRESULT RegisterOleFragment(FSSpec *spec, BOOL fRegister);
  35.  
  36. typedef HRESULT ( *LPFNREGISTERSERVER)(void);
  37.  
  38.  
  39. CCodeDownload::CCodeDownload()
  40. {
  41.     mRefCount = 1;            // reference count of object
  42.     mEnumKeyP = 0;
  43.     mSiteP = NULL;
  44.     mQueueP = NULL;
  45.     mDownloadResult = 0;
  46.     mStreamDataP = NULL;
  47.     mProcessINF = false;
  48.     mEnd = NULL;
  49.     mCurrent = NULL;
  50.     mLastSectionMatch = NULL;
  51.     mStreamLength = 0;
  52. }
  53.  
  54. CCodeDownload::~CCodeDownload()
  55. {
  56. }
  57.  
  58. //
  59. //  CCodeDownload::QueueDownload
  60. //
  61.  
  62. void
  63. CCodeDownload::QueueDownload(CXSite *inSiteP)
  64. {
  65.     QUEUED_DOWNLOAD *QueueEntryP;
  66.  
  67.     if (mSiteP == NULL) {
  68.         //  No downloads are in progress, likely because this is the first one
  69.         //  in, so just start it up.
  70.         StartDownload(inSiteP);
  71.     } else {
  72.         if ((QueueEntryP = (QUEUED_DOWNLOAD *)
  73.             CoTaskMemAlloc(sizeof(QUEUED_DOWNLOAD))) != NULL) {
  74.             QueueEntryP->SiteP = inSiteP;
  75.             QueueEntryP->NextP = mQueueP;
  76.             mQueueP = QueueEntryP;
  77.         }
  78.     }
  79. }
  80.  
  81. //
  82. //  CCodeDownload::StartNextDownload
  83. //
  84.  
  85. void
  86. CCodeDownload::StartNextDownload(void)
  87. {
  88.     QUEUED_DOWNLOAD *QueueEntryP;
  89.     CXSite *SiteP;
  90.     HRESULT        hr;
  91.  
  92.     while (mQueueP != NULL)
  93.     {
  94.         //  Unlink the queued entry from the list.
  95.         QueueEntryP = mQueueP;
  96.         mQueueP = QueueEntryP->NextP;
  97.         SiteP = QueueEntryP->SiteP;
  98.         CoTaskMemFree(QueueEntryP);
  99.  
  100.         //  Check if the object still requires a download.  The download may no
  101.         //  longer be necessary if another site already downloaded all the code
  102.         //  that this site requires.
  103.         hr = SiteP->CreateControl(false);
  104.         if (hr == REGDB_E_CLASSNOTREG || hr == CO_E_DLLNOTFOUND) 
  105.         {
  106.             if (StartDownload(SiteP))
  107.                 return;
  108.         }
  109.         SiteP->Release();
  110.     }
  111.  
  112. }
  113.  
  114. //
  115. //  CCodeDownload::StartDownload
  116. //
  117. //  Fires off a new code download cycle.
  118. //
  119. //  Note as a result of being called from StartNextDownload, we may not be
  120. //  running in the "context" of the Netscape plugin that this pSite relates to.
  121. //
  122.  
  123. BOOL
  124. CCodeDownload::StartDownload(CXSite *inSiteP)
  125. {
  126.     BOOL    fSuccess = false;
  127.     FILEXTN    FileType = GetFileTypeFromExtension(inSiteP->m_pszCodeURL);
  128.  
  129.     mProcessINF = FileType == FILEXTN_INF;
  130.     mSiteP = inSiteP;            //  This is the current download site
  131.     if(FileType != FILEXTN_UNKNOWN)
  132.         fSuccess = OpenStream(inSiteP, mProcessINF, inSiteP->m_pszCodeURL);
  133.     //  The site no longer needs this memory, so free it up now.
  134. //    CoTaskMemFree(inSiteP->m_pszCodeURL);
  135.     DisposePtr(inSiteP->m_pszCodeURL);
  136.     inSiteP->m_pszCodeURL = NULL;
  137.     if(!fSuccess)
  138.     {
  139.         if(mSiteP)
  140.             mSiteP->Release();
  141.         mSiteP = NULL;
  142.     }
  143.     return fSuccess;
  144. }
  145.  
  146. BOOL
  147. CCodeDownload::OpenStream(CXSite *inSiteP, Boolean IsINFFile, LPOLESTR URLString)
  148. {
  149.     BOOL        fSuccess = FALSE;
  150.     
  151.     {
  152.         LPBINDHOST            BindSiteP;
  153.         LPENUMFORMATETC        pEnum = NULL;
  154.         LPMONIKER            URLMoniker = NULL;
  155.         HRESULT                hr = E_FAIL;
  156.         LPBINDCTX            BindContext = NULL;
  157.  
  158.         inSiteP->QueryInterface(IID_IBindHost, (LPVOID *) &BindSiteP);
  159.  
  160.         if(BindSiteP)
  161.         {
  162.             
  163.             BindSiteP->CreateMoniker((LPOLESTR)URLString, NULL, &URLMoniker, 0);
  164.             if(URLMoniker)
  165.             {
  166.                 LPVOID                Dummy;
  167.                 
  168.                 // if inf file, don't register enumerator so we get a stream not a file
  169.                 if(!IsINFFile)
  170.                 {
  171.                     CreateFormatEnumerator(1, &g_FileFormatEtc, &pEnum);
  172.                     if(pEnum)
  173.                     {
  174.                         CreateBindCtx( 0, &BindContext);
  175.                         if(BindContext)
  176.                             RegisterFormatEnumerator(BindContext, pEnum, 0);
  177.                     }
  178.                 }
  179.                 if(IsINFFile || BindContext)
  180.                 {
  181.                     hr = BindSiteP->MonikerBindToStorage(URLMoniker, BindContext, 
  182.                             (IBindStatusCallback*)this, IID_IStream, &Dummy);
  183.                     if(hr == E_PENDING)
  184.                         hr = S_OK;
  185.                     if(BindContext)
  186.                         BindContext->Release();
  187.                     URLMoniker->Release();
  188.                 }
  189.                 if(pEnum)
  190.                     pEnum->Release();
  191.             }
  192.             BindSiteP->Release();
  193.         }
  194.         if(hr == S_OK)
  195.         {
  196.             fSuccess = TRUE;
  197.         }
  198.     }
  199.  
  200.     return fSuccess;
  201. }
  202.  
  203. //
  204. //  CCodeDownload::ProcessNextSection
  205. //
  206.  
  207. void
  208. CCodeDownload::ProcessNextSection(void)
  209. {
  210.     BOOL fResult;
  211.     OLECHAR szKeyName[MAX_PATH];
  212.     OLECHAR szBuffer[64];
  213.     OLECHAR szURL[MAX_URL_STRING];
  214.     BOOL fUseExtensionsFolder;
  215.     CLSID clsid;
  216.     DWORD dwMinimumVersion;
  217.  
  218.     //  Get the name of the next section from the .inf to download.
  219. ProcessNext:
  220.     if (mEnumKeyP == NULL) {
  221.         fResult = GetFirstKey("Add.Code", &mEnumKeyP, szKeyName,
  222.             sizeof(szKeyName));
  223.     } else {
  224.         fResult = GetNextKey(&mEnumKeyP, szKeyName,
  225.             sizeof(szKeyName));
  226.     }
  227.  
  228.     if (!fResult)
  229.     {
  230.         // destroy the inf file data
  231.         CoTaskMemFree(mStreamDataP);
  232.         mStreamDataP = NULL;
  233.         //  There aren't any more sections to process, so attempt to create the
  234.         //  site's object.
  235.         mSiteP->CreateControl(false);
  236.         mSiteP->Release();
  237.         mSiteP = NULL;
  238.         mEnumKeyP = NULL;
  239.         mLastSectionMatch = NULL;
  240.         goto StartNextDownload;
  241.     }
  242.  
  243.     GetProfileString("Add.Code", szKeyName, szKeyName,
  244.         sizeof(szKeyName));
  245.  
  246.     //  Get the minimum required version of the module.
  247.     dwMinimumVersion = 0;
  248.     if (GetProfileString(szKeyName, g_szFileVersion, szBuffer,
  249.         sizeof(szBuffer))) {
  250.         if (szBuffer[0] != '\0') {
  251.             //  If incorrectly formed, the following will return FALSE and
  252.             //  dwMinimumVersion will continue to be zero.  Should we treat that
  253.             //  as a download failure?
  254.             GetVersionFromString(szBuffer, &dwMinimumVersion);
  255.         }
  256.     }
  257.  
  258.     //  Get the folder that this file is supposed to go in-- either the ActiveX
  259.     //  Cache or the Extensions folder.
  260.     fUseExtensionsFolder = FALSE;
  261.     if (GetProfileString(szKeyName, g_szDestDir, szBuffer,
  262.         sizeof(szBuffer))) {
  263.         //  These magic numbers are LDID_WIN and LDID_SYS as defined by Windows
  264.         //  .INF files.
  265.         if (strcmp(g_szDestDir, "10") || strcmp(g_szDestDir, "11"))
  266.             fUseExtensionsFolder = TRUE;
  267.     }
  268.  
  269.     //  Get the URL that points at the code for this component.
  270.     if (!GetProfileString(szKeyName, "file-mac-ppc", szURL,
  271.         sizeof(szURL))) {
  272.         if (!GetProfileString(szKeyName, "file", szURL,
  273.             sizeof(szURL))) {
  274.             szURL[0] = '\0';
  275.         }
  276.     }
  277.  
  278.     //  If the file's URL is the magic "ignore", then this file is not required
  279.     //  for this platform, so we can skip to the next .inf line.
  280.     if (strcmp(szURL, "ignore") == 0)
  281.         goto ProcessNext;
  282.  
  283.     //  Determine if a locally installed copy of the code exists and if it's an
  284.     //  adequate version to use.
  285.     if (GetProfileString(szKeyName, "clsid", szBuffer,
  286.         sizeof(szBuffer))) {
  287.         //  If the string is incorrectly formed, bail out.
  288.         if (CLSIDFromString(szBuffer, &clsid) != S_OK)
  289.             goto StartNextDownload;
  290.         //  If the handler for this CLSID is okay, then we're fine... go to the
  291.         //  next line.
  292.         if (CheckCLSIDVersion(clsid, dwMinimumVersion))
  293.             goto ProcessNext;
  294.     } else {
  295.         //  If this file version is okay, then we're fine... go to the next line.
  296.         if (CheckFileVersion(szKeyName, dwMinimumVersion, fUseExtensionsFolder))
  297.             goto ProcessNext;
  298.     }
  299.  
  300.     //  The file extension must be BinHex... we do not support recursive .INF
  301.     //  files and we don't know how to handle any other file type.
  302.     if (GetFileTypeFromExtension(szURL) != FILEXTN_HQX)
  303.         goto StartNextDownload;
  304.  
  305.     if(OpenStream(mSiteP, false, szURL))
  306.     {
  307.         return;
  308.     }
  309.  
  310. StartNextDownload:
  311.     StartNextDownload();
  312.     return;
  313. }
  314.  
  315. //
  316. //  CCodeDownload::InfAvailable
  317. //
  318. //  Called by the .inf stream notification object when its transfer is complete.
  319. //
  320.  
  321. void
  322. CCodeDownload::InfAvailable(BOOL fSuccess)
  323. {
  324.     if (fSuccess)
  325.     {
  326.         ProcessNextSection();
  327.     }
  328.     else
  329.     {
  330.         //  If the transfer was not successful, bail on this download and start
  331.         //  the next one.
  332.         if(mSiteP)
  333.             mSiteP->Release();
  334.         mSiteP = NULL;
  335.         StartNextDownload();
  336.     }
  337. }
  338.  
  339. //
  340. //  CCodeDownload::CodeAvailable
  341. //
  342. //  Called by the code stream notification object when its transfer is complete.
  343. //
  344.  
  345. void
  346. CCodeDownload::CodeAvailable(BOOL fSuccess)
  347. {
  348.     if (fSuccess)
  349.     {
  350.         if (mProcessINF)
  351.         {
  352.             ProcessNextSection();
  353.         }
  354.         else
  355.         {
  356.             //  This was a straight code download, not driven by an .INF, so we
  357.             //  can attempt to create the object now.
  358.             mSiteP->CreateControl(false);
  359.             mSiteP->Release();
  360.             mSiteP = NULL;
  361.             StartNextDownload();
  362.         }
  363.     }
  364.     else
  365.     {
  366.         //  If the transfer was not successful, bail on this download and start
  367.         //  the next one.
  368.         if(mSiteP)
  369.             mSiteP->Release();
  370.         mSiteP = NULL;
  371.         StartNextDownload();
  372.     }
  373. }
  374.  
  375. //
  376. //  FindPrivateFolder
  377. //
  378. //  Returns the volume and directory reference of the specified folder name from
  379. //  the specified parent directory, making the directory if necessary.
  380. //
  381.  
  382. BOOL FindPrivateFolder(short *foundvRefNum, long *foundDirID, long ParentDirID, StringPtr FolderName)
  383. {
  384.     CInfoPBRec pb;
  385.     HFileParam hb;
  386.  
  387.     pb.dirInfo.ioNamePtr = FolderName;
  388.     pb.dirInfo.ioFDirIndex = 0;
  389.     pb.dirInfo.ioVRefNum = *foundvRefNum;
  390.     pb.dirInfo.ioDrDirID = ParentDirID;
  391.     pb.dirInfo.ioACUser = 0;
  392.  
  393.     if (PBGetCatInfoSync(&pb) == noErr) {
  394.         //  The Win32 code currently bails its cache folder is not a folder.
  395.         if (pb.dirInfo.ioFlAttrib & ioDirMask) {
  396.             *foundDirID = pb.dirInfo.ioDrDirID;
  397.             return TRUE;
  398.         }
  399.     } else {
  400.         hb.ioNamePtr = (StringPtr) FolderName;
  401.         hb.ioVRefNum = *foundvRefNum;
  402.         hb.ioDirID = ParentDirID;
  403.  
  404.         if (PBDirCreateSync((union HParamBlockRec *) &hb) == noErr) {
  405.             *foundDirID = hb.ioDirID;
  406.             return TRUE;
  407.         }
  408.     }
  409.  
  410.     return FALSE;
  411. }
  412.  
  413. //
  414. //  FindExplorerFolder
  415. //
  416. //  Returns the volume and directory reference of the Internet Explorer folder.
  417. //
  418.  
  419. BOOL FindExplorerFolder(short *foundvRefNum, long *foundDirID)
  420. {
  421.     BOOL fResult;
  422.     long ParentDirID;
  423.  
  424.     if (FindFolder((short) kOnSystemDisk, kPreferencesFolderType, kCreateFolder,
  425.         foundvRefNum, &ParentDirID) == noErr) {
  426.         fResult = FindPrivateFolder(foundvRefNum, foundDirID, ParentDirID,
  427.             (StringPtr) g_szExplorerFolderName);
  428.     } else {
  429.         fResult = FALSE;
  430.     }
  431.  
  432.     return fResult;
  433. }
  434.  
  435. //
  436. //  FindActiveXCacheFolder
  437. //
  438. //  Returns the volume and directory reference of the ActiveX cache folder.
  439. //
  440.  
  441. BOOL
  442. FindActiveXCacheFolder(short *foundvRefNum, long *foundDirID)
  443. {
  444.     BOOL fResult;
  445.     long ParentDirID;
  446.  
  447.     if (FindExplorerFolder(foundvRefNum, &ParentDirID)) {
  448.         fResult = FindPrivateFolder(foundvRefNum, foundDirID, ParentDirID,
  449.             (StringPtr) g_szCacheFolderName);
  450.     } else {
  451.         fResult = FALSE;
  452.     }
  453.  
  454.     return fResult;
  455. }
  456.  
  457. //
  458. //  FindDownloadFolder
  459. //
  460. //  Returns the volume and directory reference of the ActiveX cache or the
  461. //  Extensions folder
  462. //
  463.  
  464. BOOL
  465. FindDownloadFolder(short *foundvRefNum, long *foundDirID, BOOL fExtensionsFolder)
  466. {
  467.     BOOL fResult;
  468.  
  469.     if (fExtensionsFolder) {
  470.         fResult = (FindFolder((short) kOnSystemDisk, kExtensionFolderType,
  471.             kCreateFolder, foundvRefNum, foundDirID) == noErr);
  472.     } else {
  473.         fResult = FindActiveXCacheFolder(foundvRefNum, foundDirID);
  474.     }
  475.  
  476.     return fResult;
  477. }
  478.  
  479. //
  480. //  GetFileTypeFromExtension
  481. //
  482.  
  483. FILEXTN
  484. GetFileTypeFromExtension(LPOLESTR pszURL)
  485. {
  486.     FILEXTN filextn;
  487.     LPOLESTR pszCurrent;
  488.     LPOLESTR pszExtension;
  489.  
  490.     for (pszCurrent = pszURL, pszExtension = NULL; *pszCurrent; pszCurrent++) {
  491.         if (*pszCurrent == '.') {
  492.             pszExtension = pszCurrent + 1;
  493.         } else {
  494.             if (*pszCurrent == '/' || *pszCurrent == '\\') {
  495.                    pszExtension = NULL;
  496.                }
  497.         }
  498.     }
  499.  
  500.     filextn = FILEXTN_UNKNOWN;
  501.     if (strcmp(pszExtension, "hqx") == 0) {
  502.         filextn = FILEXTN_HQX;
  503.     } else {
  504.         if (strcmp(pszExtension, "inf") == 0) {
  505.                filextn = FILEXTN_INF;
  506.            }
  507.     }
  508.  
  509.     return filextn;
  510. }
  511.  
  512. //
  513. //  GetVersionFromFile
  514. //
  515. //  Returns the version from the specified file's "vers" resource.
  516. //
  517.  
  518. BOOL
  519. GetVersionFromFile(FSSpec *spec, LPDWORD pdwVersion)
  520. {
  521.     BOOL fResult = FALSE;
  522.     short resFile;
  523.     Handle hResource;
  524.  
  525.     if ((resFile = FSpOpenResFile(spec, fsRdPerm)) != -1) {
  526.         if ((hResource = Get1Resource('vers', 1)) != nil) {
  527.             if (GetHandleSize(hResource) > sizeof(DWORD)) {
  528.                 //  Extract the version number from the resource, stripping off
  529.                 //  the release stage byte.
  530.                 *pdwVersion = (*((LPDWORD)(*hResource))) & 0xFFFF00FF;
  531.                 fResult = TRUE;
  532.             }
  533.         }
  534.         CloseResFile(resFile);
  535.     }
  536.  
  537.     return fResult;
  538. }
  539.  
  540. //
  541. //  GetVersionFromString
  542. //
  543.  
  544. BOOL
  545. GetVersionFromString(LPOLESTR pszString, LPDWORD pdwVersion)
  546. {
  547.     BYTE parts[4] = { 0, 0, 0, 0 };
  548.     UINT CurrentPart = 0;
  549.     UINT n = 0;
  550.  
  551.     while (TRUE) {
  552.         if (*pszString == ',' || *pszString == '\0') {
  553.             if (CurrentPart >= 4)
  554.                 return FALSE;
  555.             parts[CurrentPart++] = (BYTE) n;
  556.             n = 0;
  557.             if (*pszString == '\0')
  558.                 break;
  559.         } else {
  560.             if (*pszString < '0' || *pszString > '9')
  561.                 return FALSE;
  562.             //  The 'vers' resource stores numbers in BCD format.
  563.             n = n * 16 + (*pszString - '0');
  564.         }
  565.         pszString++;
  566.     }
  567.  
  568.     //  Note that we ignore the "c" element from "version=a,b,c,d" because
  569.     //  GetVersionFromFile returns a number of the form "a,b,0,d"
  570.     *pdwVersion = (((DWORD) parts[0]) << 24) + (((DWORD) parts[1]) << 16) +
  571.         (((DWORD) parts[3]) << 0);
  572.     return TRUE;
  573. }
  574.  
  575. //
  576. //  GetVersionFromURL
  577. //
  578. //  Returns the "version" from the specified URL string.  As a side effect, the
  579. //  string will have any optional parameters "stripped off" by nulling out any
  580. //  present '#' character.
  581. //
  582.  
  583. BOOL
  584. GetVersionFromURL(LPOLESTR pszURL, LPDWORD pdwVersion)
  585. {
  586.     LPOLESTR pszString;
  587.     LPOLESTR pszVersionStart;
  588.  
  589.     if ((pszString = strchr(pszURL, '#')) == NULL)
  590.         return FALSE;
  591.     *pszString++ = '\0';                //  Zap the '#' character
  592.     pszVersionStart = pszString;
  593.  
  594.     if ((pszString = strchr(pszString, '=')) == NULL)
  595.         return FALSE;
  596.     *pszString++ = '\0';                //  Zap the '=' character
  597.  
  598.     if (strcmp(pszVersionStart, g_szVersion) != 0)
  599.         return FALSE;
  600.  
  601.     return GetVersionFromString(pszString, pdwVersion);
  602. }
  603.  
  604. //
  605. //  CheckFileVersion
  606. //
  607. //  Returns TRUE if the specified file from the specified folder is at least the
  608. //  specified minimum version, else returns FALSE.
  609. //
  610.  
  611. BOOL
  612. CheckFileVersion(LPOLESTR pszFile, DWORD dwMinimumVersion, BOOL
  613.     fUseExtensionsFolder)
  614. {
  615.     BOOL fResult = FALSE;
  616.     DWORD dwFileVersion;
  617.     FSSpec spec;
  618.  
  619.     if (FindDownloadFolder(&spec.vRefNum, &spec.parID, fUseExtensionsFolder)) {
  620.         c2pstr(pszFile);
  621.         if (FSMakeFSSpec(spec.vRefNum, spec.parID, (StringPtr) pszFile,
  622.             &spec) == noErr) {
  623.             if (dwMinimumVersion != 0) {
  624.                 if (GetVersionFromFile(&spec, &dwFileVersion) &&
  625.                     (dwMinimumVersion <= dwFileVersion))
  626.                     fResult = TRUE;
  627.             } else {
  628.                 //  No minimum version specified; simply checks that the file
  629.                 //  exists.
  630.                 fResult = TRUE;
  631.             }
  632.         }
  633.         p2cstr((StringPtr) pszFile);
  634.     }
  635.  
  636.     return fResult;
  637. }
  638.  
  639.  
  640. //
  641. //  CCodeDownload::IUnknown::QueryInterface
  642. //
  643. //  Returns a pointer to the specified interface on a component to which a
  644. //  client currently holds an interface pointer.
  645. //
  646. STDMETHODIMP
  647. CCodeDownload::QueryInterface(REFIID RefID, void** Obj)
  648. {
  649.     void* pv = nil;
  650.  
  651.     if (RefID == IID_IUnknown || RefID == IID_IBindStatusCallback)
  652.         pv = (LPVOID)(IBindStatusCallback* ) this;
  653.  
  654.     *Obj = pv;
  655.     
  656.     // if we got an interface, ref it and return ok
  657.     if ( pv )
  658.     {
  659.             ((IUnknown*) pv)->AddRef();
  660.         return S_OK;
  661.     }
  662.     else
  663.         return E_NOINTERFACE;
  664. }
  665.  
  666.  
  667. //
  668. //  CCodeDownload::IUnknown::AddRef
  669. //
  670. //  Increments the reference count for the calling interface.
  671. //
  672. STDMETHODIMP_(ULONG)
  673. CCodeDownload::AddRef(void)
  674. {
  675.     return ++mRefCount;
  676. }
  677.  
  678.  
  679. //
  680. //  CCodeDownload::IUnknown::Release
  681. //
  682. //  Decrements the reference count for the calling interface on a object.  If
  683. //  the reference count on the object falls to zero, the object is freed.
  684. //
  685. STDMETHODIMP_(ULONG)
  686. CCodeDownload::Release(void)
  687. {
  688.     if (--mRefCount != 0)
  689.         return mRefCount;
  690.  
  691. //    delete this;
  692.     return 0;
  693. }
  694.  
  695. //=--------------------------------------------------------------------------=
  696. //  CCodeDownload::IBindStatusCallback::OnStartBinding
  697. //=--------------------------------------------------------------------------=
  698. //
  699. STDMETHODIMP
  700. CCodeDownload::OnStartBinding(DWORD BSCOption, IBinding* aBinding)
  701. {
  702. #pragma unused (BSCOption, aBinding)
  703. //    m_pib->AddRef();
  704.     mDownloadResult = FALSE;
  705.  
  706.     return S_OK;
  707. }
  708.  
  709.  
  710. //=--------------------------------------------------------------------------=
  711. //  CCodeDownload::IBindStatusCallback::GetPriority
  712. //=--------------------------------------------------------------------------=
  713. //
  714. STDMETHODIMP
  715. CCodeDownload::GetPriority(LONG *Priority)
  716. {
  717.     *Priority = 0;
  718.     return S_OK;
  719. }
  720.  
  721.  
  722. //=--------------------------------------------------------------------------=
  723. //  CCodeDownload::IBindStatusCallback::OnLowResource
  724. //=--------------------------------------------------------------------------=
  725. //
  726. STDMETHODIMP
  727. CCodeDownload::OnLowResource(DWORD reserved)
  728. {
  729. #pragma unused (reserved)
  730.     return S_OK;
  731. }
  732.  
  733.  
  734. //=--------------------------------------------------------------------------=
  735. //  CCodeDownload::IBindStatusCallback::OnProgress
  736. //=--------------------------------------------------------------------------=
  737. //
  738. STDMETHODIMP
  739. CCodeDownload::OnProgress(ULONG Progress, ULONG ProgressMax, ULONG StatusCode, const char* StatusText)
  740. {
  741. #pragma unused (Progress, ProgressMax, StatusCode, StatusText)
  742.     return S_OK;
  743. }
  744.  
  745.  
  746. //=--------------------------------------------------------------------------=
  747. //  CCodeDownload::IBindStatusCallback::OnStopBinding
  748. //=--------------------------------------------------------------------------=
  749. //
  750. STDMETHODIMP
  751. CCodeDownload::OnStopBinding(HRESULT hresult, const char* Error)
  752. {
  753. #pragma unused (hresult, Error)
  754. //    if (m_pib != NULL)
  755. //        m_pib->Release();
  756. //    m_pib = NULL;
  757.     
  758.     CodeAvailable(mDownloadResult);
  759.     return S_OK;
  760. }
  761.  
  762.  
  763.  
  764. //=--------------------------------------------------------------------------=
  765. //  CCodeDownload::IBindStatusCallback::GetBindInfo
  766. //=--------------------------------------------------------------------------=
  767. //
  768. STDMETHODIMP
  769. CCodeDownload::GetBindInfo(DWORD* BINDF, BINDINFO *BindInfo)
  770. {
  771.     *BINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE;
  772.     BindInfo->dwBindVerb = BINDVERB_GET;
  773.     return S_OK;
  774. }
  775.  
  776.  
  777. //=--------------------------------------------------------------------------=
  778. //  CCodeDownload::IBindStatusCallback::OnDataAvailable
  779. //=--------------------------------------------------------------------------=
  780. //
  781. STDMETHODIMP
  782. CCodeDownload::OnDataAvailable(DWORD BSCF, DWORD Size, FORMATETC* FormatEtc, STGMEDIUM* StgMedium)
  783. {
  784. #pragma unused (FormatEtc)
  785.     if (StgMedium->tymed == TYMED_FILE)
  786.     {
  787.         Str255    fileName;
  788.         FSSpec    BinHexFile;
  789.         FSSpec    UnpackedFile;
  790.  
  791.         if (StgMedium->lpszFileName != NULL) {
  792.             strncpy((char *) fileName, StgMedium->lpszFileName, 256);
  793.             c2pstr((char *) fileName);
  794.  
  795.             if (FSMakeFSSpec(0, 0, fileName, &BinHexFile) == noErr)
  796.             {
  797.                 if (IsBinHexFile(&BinHexFile, &UnpackedFile))
  798.                 {
  799.                     if (FindDownloadFolder(&UnpackedFile.vRefNum,
  800.                         &UnpackedFile.parID, false))
  801.                     {
  802.                         if (UnpackBinHexFile(&BinHexFile, &UnpackedFile, NULL))
  803.                         {
  804.                             RegisterOleFragment(&UnpackedFile, TRUE);
  805.                             mDownloadResult = TRUE;
  806.                         }
  807.                     }
  808.                 }
  809.             }
  810.         }
  811.  
  812.         CoTaskMemFree(StgMedium->lpszFileName);
  813.     }
  814.     else if ((StgMedium->tymed == TYMED_ISTREAM) && ((BSCF & BSCF_LASTDATANOTIFICATION) == 0))
  815.     {
  816.         if(!mStreamDataP)
  817.         {
  818.             mStreamDataP = (unsigned char *)CoTaskMemAlloc(Size);
  819.             mStreamLength = 0;
  820.         }
  821.         else
  822.             mStreamDataP = (unsigned char *) CoTaskMemRealloc(mStreamDataP, mStreamLength + Size);
  823.             
  824.         StgMedium->pstm->Read(mStreamDataP + mStreamLength, Size, NULL);
  825.         mStreamLength += Size;
  826.         mDownloadResult = TRUE;
  827.         mEnd = (LPOLESTR) mStreamDataP + mStreamLength;
  828.     }
  829.     if (StgMedium->pUnkForRelease != NULL)
  830.         StgMedium->pUnkForRelease->Release();
  831.  
  832.     return S_OK;
  833. }
  834.  
  835.  
  836. //=--------------------------------------------------------------------------=
  837. //  CCodeDownload::IBindStatusCallback::OnObjectAvailable
  838. //=--------------------------------------------------------------------------=
  839. //
  840. STDMETHODIMP
  841. CCodeDownload::OnObjectAvailable(REFIID RefID, IUnknown* Unknown)
  842. {
  843. #pragma unused (RefID, Unknown)
  844.     return S_OK;
  845. }
  846.  
  847. //
  848. //  RegisterOleFragment
  849. //
  850. //  Calls the DllRegisterServer or DllUnregisterServer entrypoint of the
  851. //  specified file.
  852. //
  853.  
  854. HRESULT
  855. RegisterOleFragment(FSSpec *spec, BOOL fRegister)
  856. {
  857.     HRESULT                hr;
  858.     CFragConnectionID    connID;
  859.     Ptr                    mainAddr;
  860.     Str255                errName;
  861.     StringPtr            psymName;
  862.     Ptr                    symAddr;
  863.     CFragSymbolClass    symClass;
  864.  
  865.     if (GetDiskFragment(spec, 0, kCFragGoesToEOF, nil, kLoadCFrag, &connID, &mainAddr,
  866.         errName) == noErr) {
  867.         psymName = fRegister ? "\pDllRegisterServer" : "\pDllUnregisterServer";
  868.         if (FindSymbol(connID, psymName, &symAddr, &symClass) == noErr) {
  869.             //  DllRegisterServer/DllUnregisterServer have the same prototype.
  870.             hr = ((LPFNREGISTERSERVER) symAddr)();
  871.         } else {
  872.             //  We won't consider it an error if this fragment isn't self-
  873.             //  registering.
  874.             hr = S_OK;
  875.         }
  876.         CloseConnection(&connID);
  877.     } else {
  878.         hr = ResultFromScode(CO_E_DLLNOTFOUND);
  879.     }
  880.  
  881.     return hr;
  882. }
  883.  
  884. #define IsSpace(ch)     ((ch)==' ' || (ch)=='\t' || (ch)=='\n' || (ch)=='\r')
  885. #define IsEndOfLine(ch) ((ch)=='\n' || (ch)=='\r')
  886. #define ToUpper(ch)     (((ch)>='A'&&(ch)<='Z') ? ((ch)-'A'+'a') : (ch))
  887. #define min(a,b)        (((a) < (b)) ? (a) : (b))
  888.  
  889. //
  890. //  CInfStreamNotify::SkipWhitespace
  891. //
  892. //  Moves the current pointer ahead to the first non-whitespace character.
  893. //
  894.  
  895. void
  896. CCodeDownload::SkipWhitespace(void)
  897. {
  898.     while (IsSpace(*mCurrent) && mCurrent < mEnd)
  899.         mCurrent++;
  900. }
  901.  
  902. //
  903. //  CCodeDownload::SkipToNextLine
  904. //
  905. //  Moves the current pointer ahead to the first non-whitespace character after
  906. //  a end-of-line character.
  907. //
  908.  
  909. void
  910. CCodeDownload::SkipToNextLine(void)
  911. {
  912.     char ch;
  913.     do {
  914.         while (mCurrent < mEnd) {
  915.             ch = *mCurrent++;
  916.             if (IsEndOfLine(ch))
  917.                 break;
  918.         }
  919.         SkipWhitespace();
  920.     }   while (mCurrent < mEnd && *mCurrent == ';');
  921. }
  922.  
  923. //
  924. //  CCodeDownload::FindSection
  925. //
  926. //  Moves the current pointer ahead to the first line after the section that
  927. //  matches the specified section name.  Returns TRUE if the section was found,
  928. //  else FALSE.
  929. //
  930.  
  931. BOOL
  932. CCodeDownload::FindSection(LPOLESTR pszSection)
  933. {
  934.     BOOL fSecondPass;
  935.     LPOLESTR pszStop;
  936.     LPOLESTR pszSectionMatch;
  937.     LPOLESTR pszSectionStart;
  938.  
  939.     fSecondPass = FALSE;
  940.  
  941.     //  Start scanning from the start of the memory buffer or from the last
  942.     //  section where we found a match as an optimization.
  943.     mCurrent = (mLastSectionMatch != NULL) ? mLastSectionMatch :
  944.         (LPOLESTR) mStreamDataP;
  945.     pszStop = mEnd;
  946.  
  947. DoNextPass:
  948.     SkipWhitespace();
  949.     while (mCurrent < pszStop) {
  950.         //  As an optimization, if this is the section we're looking for,
  951.         //  we'll keep a pointer to it so that the next time FindSection is
  952.         //  called, we can start from there and catch multiple requests for
  953.         //  the same section.
  954.         pszSectionStart = mCurrent;
  955.         if (*mCurrent++ == '[') {
  956.             pszSectionMatch = pszSection;
  957.             while (mCurrent < pszStop && ToUpper(*mCurrent) ==
  958.                 ToUpper(*pszSectionMatch)) {
  959.                 mCurrent++;
  960.                 pszSectionMatch++;
  961.                 if (*pszSectionMatch == '\0') {
  962.                     if (mCurrent < pszStop && *mCurrent++ == ']') {
  963.                         mLastSectionMatch = pszSectionStart;
  964.                         SkipToNextLine();
  965.                         return TRUE;
  966.                     }
  967.                     break;
  968.                 }
  969.             }
  970.         }
  971.         SkipToNextLine();
  972.     }
  973.  
  974.     //  If this is our first pass and we didn't begin from the base of the
  975.     //  memory buffer, then do another pass, this time from the base of the
  976.     //  buffer.
  977.     if (!fSecondPass && mLastSectionMatch != NULL) {
  978.         mCurrent = (LPOLESTR) mStreamDataP;
  979.         pszStop = mLastSectionMatch;
  980.         fSecondPass = TRUE;
  981.         goto DoNextPass;
  982.     }
  983.  
  984.     return FALSE;
  985. }
  986.  
  987. //
  988. //  CCodeDownload::GetProfileString
  989. //
  990. //  Reads a string from the .inf file much like the Win32 GetProfileString API.
  991. //  Returns TRUE if the specified section/key were found, else FALSE.
  992. //
  993.  
  994. BOOL
  995. CCodeDownload::GetProfileString(LPOLESTR pszSection, LPOLESTR pszKey,
  996.     LPOLESTR pszBuffer, UINT BufferSize)
  997. {
  998.     LPOLESTR pszKeyMatch;
  999.     LPOLESTR pszBufferBase;
  1000.  
  1001.     //  FindSection will move the current pointer to the line immediately after
  1002.     //  the section name, if the section exists.
  1003.     if (!FindSection(pszSection))
  1004.         return FALSE;
  1005.  
  1006.     while (mCurrent < mEnd && *mCurrent != '[') {
  1007.         pszKeyMatch = pszKey;
  1008.         while (mCurrent < mEnd && ToUpper(*mCurrent) ==
  1009.             ToUpper(*pszKeyMatch)) {
  1010.             mCurrent++;
  1011.             pszKeyMatch++;
  1012.             if (*pszKeyMatch == '\0') {
  1013.                 SkipWhitespace();
  1014.                 if (mCurrent < mEnd && *mCurrent == '=') {
  1015.                     mCurrent++;
  1016.                     SkipWhitespace();
  1017.                     pszBufferBase = pszBuffer;
  1018.                     while (mCurrent < mEnd &&
  1019.                         !IsEndOfLine(*mCurrent) && *mCurrent != ';' &&
  1020.                         BufferSize-- > 0) {
  1021.                         *pszBuffer++ = *mCurrent++;
  1022.                     }
  1023.                     //  Strip off any trailing whitespace from the buffer.
  1024.                     while (pszBuffer > pszBufferBase && IsSpace(*(pszBuffer-1)))
  1025.                         pszBuffer--;
  1026.                     *pszBuffer = '\0';
  1027.                     return TRUE;
  1028.                 }
  1029.                 break;
  1030.             }
  1031.         }
  1032.         SkipToNextLine();
  1033.     }
  1034.  
  1035.     return FALSE;
  1036. }
  1037.  
  1038. //
  1039. //  CCodeDownload::GetKeyCommon
  1040. //
  1041. //  Common worker routine for GetFirstKeyName and GetNextKeyName.  On entry, the
  1042. //  current pointer should be positioned at the first non-whitespace character
  1043. //  of the next candidate line.
  1044. //
  1045.  
  1046. BOOL
  1047. CCodeDownload::GetKeyCommon(LPVOID *pvEnumKey, LPOLESTR pszBuffer, UINT
  1048.     BufferSize)
  1049. {
  1050.     LPOLESTR pszKeyStart;
  1051.     UINT BytesToCopy;
  1052.  
  1053.     while (mCurrent < mEnd && *mCurrent != '[') {
  1054.         pszKeyStart = mCurrent;
  1055.         while (mCurrent < mEnd && !IsEndOfLine(*mCurrent)) {
  1056.             if (*mCurrent == '=') {
  1057.                 while (mCurrent > pszKeyStart && IsSpace(*(mCurrent-1)))
  1058.                     mCurrent--;
  1059.                 BytesToCopy = min((UINT) (mCurrent - pszKeyStart),
  1060.                     BufferSize - 1);
  1061.                 memcpy(pszBuffer, pszKeyStart, BytesToCopy);
  1062.                 pszBuffer[BytesToCopy] = '\0';
  1063.                 *pvEnumKey = (LPVOID) pszKeyStart;
  1064.                 return TRUE;
  1065.             }
  1066.             mCurrent++;
  1067.         }
  1068.         SkipToNextLine();
  1069.     }
  1070.  
  1071.     return FALSE;
  1072. }
  1073.  
  1074. //
  1075. //  CCodeDownload::GetFirstKey
  1076. //
  1077. //  Returns the first key from the specified section.  On return, pvEnumKey is a
  1078. //  "handle" that can be used in subsequents calls to GetNextKey.
  1079. //
  1080.  
  1081. BOOL
  1082. CCodeDownload::GetFirstKey(LPOLESTR pszSection, LPVOID *pvEnumKey, LPOLESTR
  1083.     pszBuffer, UINT BufferSize)
  1084. {
  1085.     //  FindSection will move the current pointer to the line immediately after
  1086.     //  the section name, if the section exists.
  1087.     if (!FindSection(pszSection))
  1088.         return FALSE;
  1089.  
  1090.     return GetKeyCommon(pvEnumKey, pszBuffer, BufferSize);
  1091. }
  1092.  
  1093. //
  1094. //  CCodeDownload::GetNextKey
  1095. //
  1096. //  Returns the next key from the specified section using the "handle" returned
  1097. //  from previous GetFirstKey or GetNextKey calls.
  1098. //
  1099.  
  1100. BOOL
  1101. CCodeDownload::GetNextKey(LPVOID *pvEnumKey, LPOLESTR pszBuffer, UINT
  1102.     BufferSize)
  1103. {
  1104.     //  On entry, pvEnumKey points at the line that contained the last
  1105.     //  successful enumeration.
  1106.     mCurrent = (LPOLESTR) *pvEnumKey;
  1107.     SkipToNextLine();
  1108.  
  1109.     return GetKeyCommon(pvEnumKey, pszBuffer, BufferSize);
  1110. }
  1111.  
  1112. //
  1113. //  CheckCLSIDVersion
  1114. //
  1115. //  Returns TRUE if the inproc server for the specified CLSID is at least the
  1116. //  specified minimum version, else returns FALSE.
  1117. //
  1118.  
  1119. BOOL
  1120. CheckCLSIDVersion(REFCLSID /* rclsid */, DWORD /* dwMinimumVersion */)
  1121. {
  1122.     BOOL fResult = FALSE;
  1123. #if 0   
  1124. //  BUGBUG
  1125.     FSSpec spec;
  1126.     DWORD dwFileVersion;
  1127.     if (SUCCEEDED(CoGetClassFSSpec(rclsid, CLSCTX_INPROC_SERVER, &spec))
  1128.     {
  1129.         if (dwMinimumVersion != 0)
  1130.         {
  1131.             if (GetVersionFromFile(&spec, &dwFileVersion) &&
  1132.                 (dwMinimumVersion <= dwFileVersion))
  1133.                 fResult = TRUE;
  1134.         }
  1135.         else
  1136.         {
  1137.             //  No minimum version specified; simply checks that a handler for
  1138.             //  the CLSID exists.
  1139.             fResult = TRUE;
  1140.         }
  1141.     }
  1142. #endif
  1143.     return fResult;
  1144. }
  1145.